package impl.reasoning;

import impl.core.UDecideManager;
import impl.util.UDecideLog;
import impl.util.UtilityAssertion;

import java.net.ConnectException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collection;
import java.util.HashMap;

import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLClassExpression;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyCreationException;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.owllink.OWLlinkHTTPXMLReasonerFactory;
import org.semanticweb.owlapi.owllink.OWLlinkReasonerConfiguration;
import org.semanticweb.owlapi.owllink.OWLlinkReasonerIOException;
import org.semanticweb.owlapi.reasoner.OWLReasoner;

import exceptions.UDecideInvalidURLException;
import exceptions.UDecideReasonerConnectionException;
import exceptions.UDecideReasonerConnectionRefusedException;

public class UDecideReasonerFactory {

	public enum UDecideReasonerType {
		KONCLUDE
	}

	private static String	serverAddress	= "http://localhost:8080";

	public static UDecideReasoner getUDecideReasoner(OWLOntology kb, OWLClassExpression choiceClassExpression, Collection<UtilityAssertion> collection,
			UDecideReasonerType type) throws UDecideReasonerConnectionException {
		switch (type) {
			case KONCLUDE:
				return buildKoncludeReasoner(kb, choiceClassExpression, collection);
			default:
				return buildKoncludeReasoner(kb, choiceClassExpression, collection);
		}
	}

	private static UDecideReasoner buildKoncludeReasoner(OWLOntology kb, OWLClassExpression choiceClassExpression, Collection<UtilityAssertion> uAssertions)
			throws UDecideReasonerConnectionException {
		OWLDataFactory fac = UDecideManager.getInstance().getOWLlDataFactory();
		OWLOntologyManager man = OWLManager.createOWLOntologyManager();
		OWLOntology kbClone = null;

		try {
			if (UDecideManager.fastKoncludeMode) {
				kbClone = man.createOntology(IRI.create("http://dbpedia.org/"));
			} else {
				kbClone = man.createOntology(kb.getAxioms(), kb.getOntologyID().getOntologyIRI());
			}
		} catch (OWLOntologyCreationException ex) {
			ex.printStackTrace();
			return null;
		}

		HashMap<UtilityAssertion, OWLClass> dummyClasses = new HashMap<UtilityAssertion, OWLClass>();

		int i = 0;

		for (UtilityAssertion u : uAssertions) {
			OWLClassExpression clEx = u.getClassExpression();
			OWLClassExpression targetClassExpression = fac.getOWLObjectIntersectionOf(clEx, choiceClassExpression); // additionally
			// limit
			// instances
			// to
			// choice
			// class
			// expression
			OWLClass dummyClass = fac.getOWLClass(IRI.create(kbClone.getOntologyID().getOntologyIRI() + "uDecideTmpClass" + (i++)));
			OWLAxiom equiAxiom = fac.getOWLEquivalentClassesAxiom(dummyClass, targetClassExpression);

			dummyClasses.put(u, dummyClass);
			man.addAxiom(kbClone, equiAxiom);
		}

		try {
			OWLlinkReasonerConfiguration reasonerConfiguration = new OWLlinkReasonerConfiguration(new URL(serverAddress));
			OWLlinkHTTPXMLReasonerFactory factory = new OWLlinkHTTPXMLReasonerFactory();
			UDecideLog.info("Trying to connect to Konclude reasoner on address " + serverAddress + ".");
			OWLReasoner owlReasoner = factory.createNonBufferingReasoner(kbClone, reasonerConfiguration);
			UDecideLog.info("Successfully connected to Konclude reasoner on address " + serverAddress + ".");
			// OWLClassExpression classEx =
			// UDecideManager.getInstance().getChoiceClassExpression();

			return new UDecideReasoner(owlReasoner, uAssertions, dummyClasses);
		} catch (MalformedURLException e) {
			UDecideLog.error("Not a valid URL: " + serverAddress + ".");
			throw new UDecideInvalidURLException(e.getMessage(), serverAddress);
		} catch (OWLlinkReasonerIOException e) {
			if (e.getCause() instanceof ConnectException) {
				throw new UDecideReasonerConnectionRefusedException(e.getMessage());
			} else {
				throw new UDecideReasonerConnectionException(e.getMessage());
			}

		}
	}

	public static String getServerAddress() {
		return serverAddress;
	}

	public static void setServerAddress(String serverAddress) {
		serverAddress = serverAddress;
	}
}
